package com.and.video.service;

import com.and.video.dao.VideoDao;
import com.and.video.domain.*;
import com.and.video.domain.exception.ConditionException;
import com.and.video.service.util.FastDFSUtil;
import com.and.video.service.util.ImageUtil;
import com.and.video.service.util.IpUtil;
import com.netflix.discovery.converters.Auto;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class VideoService {

    @Autowired
    private VideoDao videoDao;

    @Autowired
    private FastDFSUtil fastDFSUtil;

    @Autowired
    private UserCoinService userCoinService;

    @Autowired
    private UserService userService;
    private static final int FRAME_NO = 256;

    @Autowired
    private FileService fileService;

    @Autowired
    private ImageUtil imageUtil;


    @Transactional
    // 添加视频及其关联的标签
    public void addVideos(Video video) {
        // 设置创建时间
        Date now = new Date();
        video.setCreateTime(new Date());

        // 将视频信息插入数据库
        videoDao.addVideos(video);

        // 获取数据库自动生成的主键ID（视频ID）
        Long videoId = video.getId();

        // 从视频对象中获取标签列表
        List<VideoTag> tagList = video.getVideoTagList();

        // 遍历标签列表，为每个标签设置必要的属性
        tagList.forEach(item -> {
            // 设置标签的创建时间（使用统一的当前时间）
            item.setCreateTime(now);

            // 设置标签关联的视频ID
            item.setVideoId(videoId);
        });

        // 批量将标签列表插入到数据库
        videoDao.batchAddVideoTags(tagList);
    }


    // 定义分页查询视频的方法
    public PageResult<Video> pageListVideos(Integer size, Integer no, String area) {

        // 参数校验：检查每页大小(size)和页码(no)是否为null
        if(size == null || no == null){
            throw new ConditionException("参数异常！");
        }

        Map<String, Object> params = new HashMap<>();
        // 计算分页起始位置：(页码-1)*每页大小
        params.put("start", (no-1)*size);
        // 设置每页大小
        params.put("limit", size);
        // 设置类型
        params.put("area", area);


        List<Video> list = new ArrayList<>();
        // 获取符合条件的视频总数
        Integer total = videoDao.pageCountVideos(params);

        // 如果总数大于0，才查询当前页的数据列表
        if(total > 0){
            list = videoDao.pageListVideos(params);
        }

        return new PageResult<>(total, list);
    }


    // 视频在线分段观看
    public void viewVideoOnlineBySlices(HttpServletRequest request,
                                        HttpServletResponse response,
                                        String url) {
        try{
            fastDFSUtil.viewVideoOnlineBySlices(request, response, url);
        }catch (Exception ignored){

        }
    }

    // 添加视频点赞的方法
    public void addVideoLike(Long videoId, Long userId) {
        // 根据视频ID查询视频信息
        Video video = videoDao.getVideoById(videoId);

        // 如果视频不存在，抛出异常
        if(video == null){
            throw new ConditionException("非法视频！");
        }

        // 检查用户是否已经点赞过该视频
        VideoLike videoLike = videoDao.getVideoLikeByVideoIdAndUserId(videoId, userId);

        // 如果已经点赞过
        if(videoLike != null){
            throw new ConditionException("已经赞过！");
        }


        videoLike = new VideoLike();
        videoLike.setVideoId(videoId);
        videoLike.setUserId(userId);
        videoLike.setCreateTime(new Date());


        videoDao.addVideoLike(videoLike);
    }

    // 删除视频点赞的方法
    public void deleteVideoLike(Long videoId, Long userId) {
        // 删除指定用户对指定视频的点赞
        videoDao.deleteVideoLike(videoId, userId);
    }

    // 获取视频点赞信息的方法
    public Map<String, Object> getVideoLikes(Long videoId, Long userId) {
        // 获取视频的总点赞数
        Long count = videoDao.getVideoLikes(videoId);

        // 检查当前用户是否已点赞该视频
        VideoLike videoLike = videoDao.getVideoLikeByVideoIdAndUserId(videoId, userId);

        // 如果videoLike不为null，表示用户已点赞
        boolean like = videoLike != null;

        // 创建返回结果Map
        Map<String, Object> result = new HashMap<>();
        // 放入总点赞数
        result.put("count", count);
        // 放入当前用户是否点赞的状态
        result.put("like", like);

        // 返回结果
        return result;
    }

    // 添加视频收藏的方法
    @Transactional
    public void addVideoCollection(VideoCollection videoCollection, Long userId) {
        // 从视频收藏对象中获取视频ID和分组ID
        Long videoId = videoCollection.getVideoId();
        Long groupId = videoCollection.getGroupId();

        // 检查参数是否有效，如果视频ID或分组ID为空，抛出异常
        if(videoId == null || groupId == null){
            throw new ConditionException("参数异常！");
        }

        // 根据视频ID查询视频信息
        Video video = videoDao.getVideoById(videoId);

        // 如果视频不存在，抛出异常
        if(video == null){
            throw new ConditionException("非法视频！");
        }

        // 删除用户原有的该视频收藏（实现更新收藏的效果）
        videoDao.deleteVideoCollection(videoId, userId);

        // 设置收藏的用户ID
        videoCollection.setUserId(userId);
        // 设置收藏创建时间为当前时间
        videoCollection.setCreateTime(new Date());

        // 添加新的视频收藏到数据库
        videoDao.addVideoCollection(videoCollection);
    }

    // 删除视频收藏的方法
    public void deleteVideoCollection(Long videoId, Long userId) {
        // 调用数据访问层方法，删除指定用户对指定视频的收藏
        videoDao.deleteVideoCollection(videoId, userId);
    }

    // 获取视频收藏信息的方法
    public Map<String, Object> getVideoCollections(Long videoId, Long userId) {
        // 获取视频的总收藏数
        Long count = videoDao.getVideoCollections(videoId);

        // 检查当前用户是否已收藏该视频
        VideoCollection videoCollection = videoDao.getVideoCollectionByVideoIdAndUserId(videoId, userId);

        // 如果videoCollection不为null，表示用户已收藏
        boolean like = videoCollection != null;

        // 创建返回结果Map
        Map<String, Object> result = new HashMap<>();
        // 放入总收藏数
        result.put("count", count);
        // 放入当前用户是否收藏的状态
        result.put("like", like);

        // 返回结果
        return result;
    }



    @Transactional
    public void addVideoCoins(VideoCoin videoCoin, Long userId) {

        Long videoId = videoCoin.getVideoId();
        // 从传入的videoCoin对象中获取要投币的数量
        Integer amount = videoCoin.getAmount();

        // 检查视频ID是否为空，如果为空则抛出异常
        if(videoId == null){
            throw new ConditionException("参数异常！");
        }

        // 通过视频ID从数据库查询视频信息
        Video video = videoDao.getVideoById(videoId);
        // 检查视频是否存在，如果不存在则抛出异常
        if(video == null){
            throw new ConditionException("非法视频！");
        }

        // 查询当前用户拥有的硬币总数
        Integer userCoinsAmount = userCoinService.getUserCoinsAmount(userId);
        // 如果查询结果为null，则视为0个硬币
        userCoinsAmount = userCoinsAmount == null ? 0 : userCoinsAmount;
        // 检查用户硬币是否足够支付本次投币
        if(amount > userCoinsAmount){
            throw new ConditionException("硬币数量不足！");
        }

        // 查询该用户是否已经给这个视频投过币
        VideoCoin dbVideoCoin = videoDao.getVideoCoinByVideoIdAndUserId(videoId, userId);

        // 如果用户是第一次给这个视频投币
        if(dbVideoCoin == null){
            // 设置用户ID
            videoCoin.setUserId(userId);
            // 设置创建时间为当前时间
            videoCoin.setCreateTime(new Date());
            // 向数据库插入新的投币记录
            videoDao.addVideoCoin(videoCoin);
        }else{
            // 如果用户已经给这个视频投过币，则获取已投币数量
            Integer dbAmount = dbVideoCoin.getAmount();
            // 将已投币数量与本次投币数量相加
            dbAmount += amount;
            // 设置用户ID
            videoCoin.setUserId(userId);
            // 设置更新后的总投币数量
            videoCoin.setAmount(dbAmount);
            // 设置更新时间为当前时间
            videoCoin.setUpdateTime(new Date());
            // 更新数据库中的投币记录
            videoDao.updateVideoCoin(videoCoin);
        }

        // 更新用户的硬币总数：减去本次投币的数量
        userCoinService.updateUserCoinsAmount(userId, (userCoinsAmount-amount));
    }

    // 定义一个方法，用于获取视频的硬币信息，返回一个包含统计数据和用户投币状态的Map
    public Map<String, Object> getVideoCoins(Long videoId, Long userId) {
        // 通过视频ID查询该视频收到的硬币总数
        Long count = videoDao.getVideoCoinsAmount(videoId);

        // 查询当前用户是否对该视频投过币
        VideoCoin videoCollection = videoDao.getVideoCoinByVideoIdAndUserId(videoId, userId);

        // 判断用户是否投过币：如果查询结果不为null，则表示用户投过币
        boolean like = videoCollection != null;

        // 创建一个HashMap用于存储返回结果
        Map<String, Object> result = new HashMap<>();

        // 将硬币总数放入结果Map中，键为"count"
        result.put("count", count);

        // 将用户是否投过币的状态放入结果Map中，键为"like"
        result.put("like", like);

        // 返回包含视频硬币信息和用户投币状态的结果Map
        return result;
    }

    public void addVideoComment(VideoComment videoComment, Long userId) {
        Long videoId = videoComment.getVideoId();
        if(videoId == null){
            throw new ConditionException("参数异常！");
        }
        Video video = videoDao.getVideoById(videoId);
        if(video == null){
            throw new ConditionException("非法视频！");
        }
        videoComment.setUserId(userId);
        videoComment.setCreateTime(new Date());
        videoDao.addVideoComment(videoComment);
    }

    // 分页查询视频评论的方法，返回包含评论列表和总数的分页结果
    public PageResult<VideoComment> pageListVideoComments(Integer size, Integer no, Long videoId) {
        // 根据视频ID查询视频信息，验证视频是否存在
        Video video = videoDao.getVideoById(videoId);
        // 如果视频不存在，抛出异常
        if(video == null){
            throw new ConditionException("非法视频！");
        }

        // 创建参数Map，用于存储分页查询条件
        Map<String, Object> params = new HashMap<>();
        // 计算分页起始位置：(页码-1)*每页大小
        params.put("start", (no - 1) * size);
        // 设置每页记录数
        params.put("limit", size);
        // 设置视频ID作为查询条件
        params.put("videoId", videoId);

        // 查询符合条件的评论总数
        Integer total = videoDao.pageCountVideoComments(params);
        // 初始化评论列表
        List<VideoComment> list = new ArrayList<>();

        // 如果有评论数据，则进行详细查询和处理
        if(total > 0){
            // 查询分页评论列表（一级评论）
            list = videoDao.pageListVideoComments(params);

            // 提取所有一级评论的ID，用于查询二级评论
            List<Long> parentIdList = list.stream().map(VideoComment::getId).collect(Collectors.toList());
            // 批量查询二级评论（回复）
            List<VideoComment> childCommentList = videoDao.batchGetVideoCommentsByRootIds(parentIdList);

            // 收集所有涉及的用户ID：
            // 1. 一级评论的用户ID
            Set<Long> userIdList = list.stream().map(VideoComment::getUserId).collect(Collectors.toSet());
            // 2. 二级评论的作者ID
            Set<Long> replyUserIdList = childCommentList.stream().map(VideoComment::getUserId).collect(Collectors.toSet());
            // 3. 二级评论中被回复的用户ID
            Set<Long> childUserIdList = childCommentList.stream().map(VideoComment::getReplyUserId).collect(Collectors.toSet());

            // 合并所有用户ID
            userIdList.addAll(replyUserIdList);
            userIdList.addAll(childUserIdList);

            // 批量查询用户信息
            List<UserInfo> userInfoList = userService.batchGetUserInfoByUserIds(userIdList);
            // 将用户信息列表转换为Map，key为用户ID，value为用户信息对象
            Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo :: getUserId, userInfo -> userInfo));

            // 处理一级评论，为其添加二级评论和用户信息
            list.forEach(comment -> {
                // 获取当前评论的ID
                Long id = comment.getId();
                // 初始化当前评论的二级评论列表
                List<VideoComment> childList = new ArrayList<>();

                // 遍历所有二级评论，找到属于当前评论的回复
                childCommentList.forEach(child -> {
                    if(id.equals(child.getRootId())){
                        // 为二级评论设置作者用户信息
                        child.setUserInfo(userInfoMap.get(child.getUserId()));
                        // 为二级评论设置被回复用户的信息
                        child.setReplyUserInfo(userInfoMap.get(child.getReplyUserId()));
                        // 将二级评论添加到当前评论的回复列表中
                        childList.add(child);
                    }
                });

                // 设置当前评论的二级评论列表
                comment.setChildList(childList);
                // 设置当前评论的作者用户信息
                comment.setUserInfo(userInfoMap.get(comment.getUserId()));
            });
        }

        // 返回分页结果，包含评论总数和当前页的评论列表
        return new PageResult<>(total, list);
    }

    // 获取视频的详细信息，返回包含视频数据和用户信息的Map
    public Map<String, Object> getVideoDetails(Long videoId) {
        // 通过视频ID从数据库查询视频的详细信息
        Video video = videoDao.getVideoDetails(videoId);

        // 从视频对象中获取发布者的用户ID
        Long userId = video.getUserId();

        // 通过用户ID查询用户的基本信息
        User user = userService.getUserInfo(userId);

        // 从用户对象中获取用户的详细信息（如昵称、头像等）
        UserInfo userInfo = user.getUserInfo();

        // 创建一个HashMap用于存储返回结果
        Map<String, Object> result = new HashMap<>();

        // 将视频详细信息放入结果Map中，键为"video"
        result.put("video", video);

        // 将用户详细信息放入结果Map中，键为"userInfo"
        result.put("userInfo", userInfo);

        return result;
    }

    public void addVideoView(VideoView videoView, HttpServletRequest request) {
        // 从videoView对象中获取用户ID和视频ID
        Long userId = videoView.getUserId();
        Long videoId = videoView.getVideoId();

        // 生成客户端标识：从请求头中获取User-Agent信息
        String agent = request.getHeader("User-Agent");
        // 使用UserAgent工具类解析User-Agent字符串
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        // 获取解析后的客户端ID（通常是浏览器或设备的唯一标识）
        String clientId = String.valueOf(userAgent.getId());

        // 获取客户端IP地址
        String ip = IpUtil.getIP(request);

        // 创建参数Map，用于查询是否已存在观看记录
        Map<String, Object> params = new HashMap<>();

        // 判断用户是否已登录（userId不为空）
        if(userId != null){
            // 如果用户已登录，使用userId作为标识参数
            params.put("userId", userId);
        }else{
            // 如果用户未登录（匿名用户），使用IP地址和客户端ID作为标识参数
            params.put("ip", ip);
            params.put("clientId", clientId);
        }

        // 获取当前日期并格式化为"yyyy-MM-dd"格式
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        params.put("today", sdf.format(now)); // 将格式化后的日期加入参数Map

        // 将视频ID加入参数Map
        params.put("videoId", videoId);

        // 根据参数查询是否已存在今天的观看记录
        VideoView dbVideoView = videoDao.getVideoView(params);

        // 如果不存在观看记录，则添加新的观看记录
        if(dbVideoView == null){
            // 设置IP地址
            videoView.setIp(ip);
            // 设置客户端ID
            videoView.setClientId(clientId);
            // 设置创建时间为当前时间
            videoView.setCreateTime(new Date());
            // 调用DAO层方法添加观看记录
            videoDao.addVideoView(videoView);
        }
        // 如果已存在观看记录，则不执行任何操作（避免重复记录）
    }


    public Integer getVideoViewCounts(Long videoId) {
        return videoDao.getVideoViewCounts(videoId);
    }


    /**
     * 基于用户的协同推荐算法实现
     * 根据用户的历史行为数据，找到相似用户群体，然后推荐相似用户喜欢的视频
     * @param userId 用户id，需要为其推荐视频的目标用户
     * @return 推荐视频列表
     */
    public List<Video> recommend(Long userId) throws TasteException {
        // 获取所有用户的偏好数据（通常是用户对视频的评分或观看行为数据）
        List<UserPreference> list = videoDao.getAllUserPreference();

        // 创建数据模型，将用户偏好数据转换为Mahout协同过滤算法可处理的格式
        DataModel dataModel = this.createDataModel(list);

        // 计算用户相似度，使用未中心化的余弦相似度算法
        UserSimilarity similarity = new UncenteredCosineSimilarity(dataModel);

        // 打印用户ID为11和12的两个用户之间的相似度（调试用）
        System.out.println(similarity.userSimilarity(11, 12));

        // 获取用户邻居（相似用户），这里设置寻找最近的2个邻居用户
        UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);

        // 获取目标用户的邻居用户ID数组
        long[] ar = userNeighborhood.getUserNeighborhood(userId);

        // 构建基于用户的推荐器
        Recommender recommender = new GenericUserBasedRecommender(dataModel, userNeighborhood, similarity);

        // 为目标用户推荐5个视频项
        List<RecommendedItem> recommendedItems = recommender.recommend(userId, 5);

        // 从推荐结果中提取视频ID
        List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());

        // 根据视频ID批量获取视频详细信息并返回
        return videoDao.batchGetVideosByIds(itemIds);
    }


    /**
     * 创建Mahout协同过滤算法所需的数据模型
     * 将用户偏好数据转换为Mahout库能够处理的DataModel格式
     * @param userPreferenceList 用户偏好数据列表，包含用户ID、视频ID和偏好值（如评分）
     * @return 构建好的DataModel对象，用于后续的协同过滤计算
     */
    private DataModel createDataModel(List<UserPreference> userPreferenceList) {
        // 创建一个FastByIDMap，用于高效存储用户偏好数据
        // Key: 用户ID, Value: 该用户的偏好数组
        FastByIDMap<PreferenceArray> fastByIdMap = new FastByIDMap<>();

        // 将用户偏好数据按用户ID分组，得到Map<用户ID, 该用户的所有偏好数据>
        Map<Long, List<UserPreference>> map = userPreferenceList.stream().collect(Collectors.groupingBy(UserPreference::getUserId));

        // 获取所有用户的分组数据（每个用户一个偏好列表）
        Collection<List<UserPreference>> list = map.values();

        // 遍历每个用户的偏好数据
        for(List<UserPreference> userPreferences : list){
            // 创建GenericPreference数组，用于存储当前用户的所有偏好项
            GenericPreference[] array = new GenericPreference[userPreferences.size()];

            // 遍历当前用户的每个偏好项
            for(int i = 0; i < userPreferences.size(); i++){
                // 获取单个用户偏好对象
                UserPreference userPreference = userPreferences.get(i);

                // 将业务层的UserPreference转换为Mahout的GenericPreference对象
                // 参数：用户ID, 视频ID, 偏好值（如评分）
                GenericPreference item = new GenericPreference(userPreference.getUserId(), userPreference.getVideoId(), userPreference.getValue());

                // 将转换后的偏好对象存入数组
                array[i] = item;
            }

            // 将当前用户的偏好数组包装为GenericUserPreferenceArray
            // 并使用用户ID作为键，存入FastByIDMap中
            // 注意：这里假设array至少有一个元素，所以使用array[0].getUserID()
            fastByIdMap.put(array[0].getUserID(), new GenericUserPreferenceArray(Arrays.asList(array)));
        }

        // 使用封装好的FastByIDMap创建并返回GenericDataModel
        return new GenericDataModel(fastByIdMap);
    }


    /**
     * 基于内容的协同推荐算法实现（物品协同过滤）
     * 根据用户对某个物品的行为，推荐与该物品相似的其他物品
     * @param userId 用户id，用于个性化推荐（可能考虑用户的历史行为）
     * @param itemId 参考内容id，基于此物品寻找相似物品
     * @param howMany 需要推荐的数量
     * @return 推荐的视频列表
     */
    public List<Video> recommendByItem(Long userId, Long itemId, int howMany) throws TasteException {
        // 获取所有用户的偏好数据（用户对视频的评分或行为数据）
        List<UserPreference> list = videoDao.getAllUserPreference();

        // 创建数据模型，将用户偏好数据转换为Mahout算法可处理的格式
        DataModel dataModel = this.createDataModel(list);

        // 计算物品（视频）之间的相似度，使用未中心化的余弦相似度算法
        ItemSimilarity similarity = new UncenteredCosineSimilarity(dataModel);

        // 创建基于物品的推荐器
        GenericItemBasedRecommender genericItemBasedRecommender = new GenericItemBasedRecommender(dataModel, similarity);

        // 获取推荐物品列表：基于用户ID和参考物品ID，推荐指定数量的相似物品
        // recommendedBecause方法返回的是因为参考物品而推荐给用户的物品列表
        // 这些物品与参考物品有较高的相似度，且可能符合用户的兴趣
        List<Long> itemIds = genericItemBasedRecommender.recommendedBecause(userId, itemId, howMany)
                .stream()
                .map(RecommendedItem::getItemID) // 从推荐结果中提取物品ID
                .collect(Collectors.toList()); // 转换为列表

        // 根据物品ID批量获取视频详细信息并返回
        return videoDao.batchGetVideosByIds(itemIds);
    }

    public List<VideoBinaryPicture> convertVideoToImage(Long videoId, String fileMd5) throws Exception{
        // 根据文件的MD5值获取文件信息
        com.and.video.domain.File file = fileService.getFileByMd5(fileMd5);

        // 构建本地临时文件路径，用于存储下载的视频文件
        String filePath = "/Users/hat/tmpfile/fileForVideoId" + videoId + "." + file.getType();

        // 从FastDFS下载视频文件到本地临时路径
        fastDFSUtil.downLoadFile(file.getUrl(), filePath);

        // 创建FFmpeg帧抓取器，用于从视频中提取帧
        FFmpegFrameGrabber fFmpegFrameGrabber = FFmpegFrameGrabber.createDefault(filePath);
        fFmpegFrameGrabber.start(); // 启动帧抓取器

        // 获取视频的总帧数
        int ffLength = fFmpegFrameGrabber.getLengthInFrames();
        Frame frame; // 用于存储抓取的帧

        // 创建帧转换器，用于将FFmpeg帧转换为Java图像对象
        Java2DFrameConverter converter = new Java2DFrameConverter();

        int count = 1; // 帧计数器，用于控制采样率
        List<VideoBinaryPicture> pictures = new ArrayList<>(); // 存储生成的剪影图片信息

        // 遍历视频的所有帧
        for(int i=1; i<= ffLength; i ++){
            // 获取当前帧的时间戳
            long timestamp = fFmpegFrameGrabber.getTimestamp();

            // 抓取当前帧的图像
            frame = fFmpegFrameGrabber.grabImage();

            // 检查是否达到采样间隔（每隔FRAME_NO帧处理一帧）
            if(count == i){
                // 检查帧是否有效
                if(frame == null){
                    throw new ConditionException("无效帧");
                }

                // 将FFmpeg帧转换为BufferedImage
                BufferedImage bufferedImage = converter.getBufferedImage(frame);

                // 将图像转换为字节数组输出流
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ImageIO.write(bufferedImage, "png", os);

                // 将字节数组转换为输入流
                InputStream inputStream = new ByteArrayInputStream(os.toByteArray());

                // 创建临时文件用于存储处理后的剪影图像
                java.io.File outputFile = java.io.File.createTempFile("convert-" + videoId + "-", ".png");

                // 调用图像处理工具，生成黑白剪影
                BufferedImage binaryImg = imageUtil.getBodyOutline(bufferedImage, inputStream);

                // 将剪影图像写入临时文件
                ImageIO.write(binaryImg, "png", outputFile);

                // 将图像中的白色部分转换为透明色（提高兼容性）
                imageUtil.transferAlpha(outputFile, outputFile);

                // 上传剪影文件到FastDFS存储
                String imgUrl = fastDFSUtil.uploadCommonFile(outputFile, "png");

                // 创建剪影图片信息对象
                VideoBinaryPicture videoBinaryPicture = new VideoBinaryPicture();
                videoBinaryPicture.setFrameNo(i); // 设置帧序号
                videoBinaryPicture.setUrl(imgUrl); // 设置图片URL
                videoBinaryPicture.setVideoId(videoId); // 设置关联的视频ID
                videoBinaryPicture.setVideoTimestamp(timestamp); // 设置帧时间戳

                // 将剪影信息添加到列表
                pictures.add(videoBinaryPicture);

                // 更新计数器，设置下一帧的处理位置
                count += FRAME_NO;

                // 删除临时文件
                outputFile.delete();
            }
        }

        // 删除下载的临时视频文件
        java.io.File tmpFile = new File(filePath);
        tmpFile.delete();

        // 批量保存剪影图片信息到数据库
        videoDao.batchAddVideoBinaryPictures(pictures);

        // 返回生成的剪影图片信息列表
        return pictures;
    }

    public List<VideoBinaryPicture> getVideoBinaryImages(Map<String, Object> params) {
        return videoDao.getVideoBinaryImages(params);
    }
}
